/*******************************************************************************
 *  Copyright (c) 2017 Oliver Meili
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Eclipse Distribution License v1.0 which accompany this distribution.
 *   
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *  The Eclipse Distribution License is available at
 *  http://www.eclipse.org/org/documents/edl-v10.php.
 *   
 *  Contributors:
 *  Oliver Meili <omi@ieee.org>
 *******************************************************************************/
package org.eclipse.vorto.codegen.ble.alpwise.templates

import com.google.common.base.Strings
import java.io.File
import java.util.UUID
import org.eclipse.vorto.codegen.api.InvocationContext
import org.eclipse.vorto.codegen.ble.model.blegatt.Service
import org.eclipse.vorto.codegen.ble.templates.BleGattTemplate
import org.eclipse.vorto.core.api.model.functionblock.FunctionblockModel

class AlpwiseServiceSourceTemplate extends BleGattTemplate<Service> {
	
	UUID uuid
	
	override getFileName(Service service) {
		return service.name + "Service.c";
	}
	
	override getPath(Service service) {
		return rootPath + File.separator + "services";
	}
	
	override getContent(Service service, InvocationContext context) {
'''
/* «service.name»Service generated by Vorto */

#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <BleTypes.h>
#include <attserver.h>

#include "BleUtils.h"
#include "BleApp_Cbk.h"

#include "«service.name»Service.h"

«service.name»Service_t «service.name»Service_Instances[NUM_«service.name.toUpperCase»_SERVICES] = {
{
	/* «service.name» Service UUID: «service.uuid» */
	.uuid = { «convertUuidToByteArray(service.uuid)» },
	.ServiceHandle = { 0 },

	«FOR ch : service.characteristics»
		/* «ch.name» characteristic properties */
		/* UUID: «ch.uuid» */
		.«ch.name.toFirstUpper» = {
					.uuid = { «convertUuidToByteArray(ch.uuid)» },
					.flags = 0x00 «IF ch.isReadable» | ATTPROPERTY_READ«ENDIF»«IF ch.isWritable» | ATTPROPERTY_WRITE«ENDIF»«IF ch.isEventable» | ATTPROPERTY_NOTIFY«ENDIF» 
		},
		«IF ch.value == null»
			.«ch.name.toFirstUpper»Value = { 0xFF },
		«ELSE»
			.«ch.name.toFirstUpper»Value = «ch.value»,
		«ENDIF»
	«ENDFOR»
}
};

static BleStatus «service.name»Service_RegisterService(«service.name»Service_t *«service.name.toLowerCase»);
static void «service.name»Service_ServiceCallback(AttServerCallbackParms* serverCallbackParms);

void «service.name»Service_Init()
{
	int i;

	for (i = 0; i < NUM_«service.name.toUpperCase»_SERVICES; i++)
	{
		«FOR ch : service.characteristics»
			«service.name»Service_Instances[i].«ch.name.toFirstUpper».type.size = ATT_UUID_SIZE_128;
			«service.name»Service_Instances[i].«ch.name.toFirstUpper».type.value.uuid128 = (uint8_t *)&(«service.name»Service_Instances[i].«ch.name.toFirstUpper».uuid);
		«ENDFOR»
		«service.name»Service_RegisterService(&(«service.name»Service_Instances[i]));
	}
}

static BleStatus «service.name»Service_RegisterService(«service.name»Service_t *«service.name.toLowerCase»)
{
	ATT_SERVER_SecureDatabaseAccess();

	/* Register the service callback for the «service.name» service */
	if (ATT_SERVER_RegisterServiceAttribute(
		ATTPDU_SIZEOF_128_BIT_UUID, (uint8_t *) «service.name.toLowerCase»->uuid,
		«service.name»Service_ServiceCallback, &(«service.name.toLowerCase»->ServiceHandle)) == BLESTATUS_FAILED)
	{
		ATT_SERVER_ReleaseDatabaseAccess();
		return BLESTATUS_FAILED;
	}

	«FOR ch : service.characteristics»
		/* Register the «ch.name.toFirstUpper» characteristic of the «service.name» Service */
		if (ATT_SERVER_AddCharacteristic(
			ATTPROPERTY_READ | ATTPROPERTY_NOTIFY,
			(Att16BitCharacteristicAttribute*) &«service.name.toLowerCase»->«ch.name.toFirstUpper».characteristic,
			&«service.name.toLowerCase»->«ch.name.toFirstUpper».type,
			ATT_PERMISSIONS_ALLACCESS,
			(sizeof(«service.name.toLowerCase»->«ch.name.toFirstUpper») / sizeof(uint8_t)),
			(uint8_t *) &«service.name.toLowerCase»->«ch.name.toFirstUpper»Value, 0, 0,
			&«service.name.toLowerCase»->ServiceHandle,
			&«service.name.toLowerCase»->«ch.name.toFirstUpper».attr) == BLESTATUS_FAILED)
		{
			ATT_SERVER_ReleaseDatabaseAccess();
			return BLESTATUS_FAILED;
		}
	«ENDFOR»

	ATT_SERVER_ReleaseDatabaseAccess();

	return BLESTATUS_SUCCESS;
}

static void «service.name»Service_ServiceCallback(AttServerCallbackParms* serverCallbackParms)
{
	printf("### «service.name» Service Callback\n");

	switch (serverCallbackParms->event) {
	case ATTEVT_SERVER_READ_REQ:
		printf("«service.name» service callback: ");
		serverCallbackParms->status = ATTSTATUS_SUCCESS;
		for (int i = 0; i < NUM_«service.name.toUpperCase»_SERVICES; i++){
			«FOR ch : service.characteristics»
				if (serverCallbackParms->parms.readReq.attribute
						== &(«service.name»Service_Instances[i].«ch.name.toFirstUpper».attr)) {
					printf("«ch.name.toFirstUpper» characteristic\n");
					«IF (ch.value == null)»
						«FOR prop : ch.properties»
							«(prop.property.eContainer.eContainer.eContainer as FunctionblockModel).name»_Cbk_«prop.property.name.toFirstUpper»(&«(prop.property.eContainer.eContainer.eContainer as FunctionblockModel).name»_Instances[i]);
						«ENDFOR»
					«ELSE»
						«service.name»Service_WriteAndNotifyValue(&«service.name»Service_Instances[i].«ch.name.toFirstUpper», &«service.name»Service_Instances[i].«ch.name.toFirstUpper»Value, «ch.length», 1);
					«ENDIF»
				}
			«ENDFOR»
		}
		break;
	default:
		break;
	}
}

void «service.name»Service_WriteAndNotifyValue(characteristicProperty_t *ch, uint8_t *value, uint8_t length, uint8_t notify)
{
	BleStatus status;

	ATT_SERVER_SecureDatabaseAccess();

	status = ATT_SERVER_WriteAttributeValue(&ch->attr, (const uint8_t *)value,
											length);

#if ENABLE_«service.name.toUpperCase»_NOTIFICATIONS

	if ((notify != 0) && (status == BLESTATUS_SUCCESS)) /* send notification */
	{
		status = ATT_SERVER_SendNotification(&ch->attr, 1);
		printf("«service.name» notification, «service.name.toFirstUpper»: %d\n", value[0] + 256*value[1]);
		printSendNotificationError(status);
	}
	else
	{
		assert(status != BLESTATUS_FAILED);

	}

#endif

	ATT_SERVER_ReleaseDatabaseAccess();

#if !ENABLE_«service.name.toUpperCase»_NOTIFICATIONS
	(void) status;
	(void) notify;
#endif
}
'''
	}

	// Result is in reverse order as required by the Alpwise BLE stack
	def String convertUuidToByteArray(String str) {
		var uuid = UUID.fromString(str);
		var result = "";
		if (!Strings.isNullOrEmpty(str)) {
			for (var i = 0; i < 8; i++) {
				result += String.format("0x%02x", new Long((uuid.getLeastSignificantBits() >> (i*8)) % 0x100).byteValue()) + ", ";
			}
			for (var i = 0; i < 8; i++) {
				result += String.format("0x%02x", new Long((uuid.getMostSignificantBits() >> (i*8)) % 0x100).byteValue());
				if (i < 7) {
					result += ", ";
				}
			}
		}	
		return result;
	}	
}
